﻿/// <copyright file="TabSendPPPoE.cs" company="not for commercial">
/// Copyright (c) 6/4/2012 All Right Reserved
/// </copyright>
/// <author>Dimitry Kaplin</author>
/// <date>6/4/2012 0:59 </date>
/// <summary>Sending pppoe packets tab logic </summary>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using EthernetTools.Utils;
using EthernetTools.Protocols.PPPoE;
using System.IO;

namespace EthernetTools.UI
{
    public partial class TabSendPPPoE : UserControl
    {
        #region Constatnt And Fields

        PppoeProtocolPacket ethPppoeProtoPacket = null;

        // presets of pasckets
        private Dictionary<string, byte[]> Presets = new Dictionary<string, byte[]>();

        string currentPresetName = "Default";

        #endregion

        public TabSendPPPoE()
        {
            InitializeComponent();

            ethPppoeProtoPacket = new PppoeProtocolPacket(MainWindow.LogWriteLine, UpdateUI);

            this.Dock = DockStyle.Fill;

            this.Disposed += new EventHandler(TabSendPPPoE_Disposed);
        }


        public void UpdateUI()
        {
            label_current_preset.Text = "Preset: " + currentPresetName;
        }

        #region Methods: Preset Operations

        /// <summary>
        /// Save current packet to preset
        /// </summary>
        /// <param name="presetName">Name of packet preseet</param>
        private void SavePreset(string presetName)
        {

            byte[] presetBytes = ethPppoeProtoPacket.ToBytesArray();
            if (presetBytes == null)
            {
                MessageBox.Show(this, "Can't save preset cause packet has errors, see log for more info", "Can't save", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (!Presets.ContainsKey(presetName))
            {
                Presets.Add(presetName, new byte[0]);
            }
            
            Presets[presetName] = presetBytes;

            currentPresetName = presetName;

            // TODO: Registry AutoSaving
        }

        /// <summary>
        /// Load from preset packet with name
        /// </summary>
        /// <param name="presetName">Name of packet preseet</param>
        /// <returns></returns>
        private bool LoadPreset(string presetName)
        {
            if (!Presets.ContainsKey(presetName))
            {
                MainWindow.LogWriteLine("There is no packet preset with name \"" + presetName + "\"", Options.Global.LogLevel.Error);
                return false;
            }

            PppoeProtocolPacket temp = PppoeProtocolPacket.ParseFromBytes(Presets[presetName]);
            if (temp == null)
            {
                MessageBox.Show(this, "Can't load preset cause packet has errors, see log for more info", "Can't load", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            ethPppoeProtoPacket.Unbind();
            
            ethPppoeProtoPacket = temp;

            ethPppoeProtoPacket.InitUiBindings(this.textBox_srcMac,
                                                this.textBox_destMac,
                                                this.textBox_playloadLength,
                                                this.textBox_ACName,
                                                this.textBox_hostUniqueID,
                                                this.textBox_sessionId,
                                                this.comboBox_ppoeType,
                                                this.treeView_pppoetags,
                                                this.label_src_convt,
                                                this.label_dst_convt);
            currentPresetName = presetName;

            // TODO: Registry AutoSaving

            return true;
        }

        /// <summary>
        /// Removes from presets packet with name
        /// </summary>
        /// <param name="presetName">Name of packet to remove</param>
        private void RemovePreset(string presetName)
        {
            Presets.Remove(presetName);
        }

        /// <summary>
        /// Save current packet preset map into file
        /// </summary>
        private void SaveAllPresets()
        {
            try
            {
                using (System.IO.FileStream fs = new System.IO.FileStream("presets.byte", System.IO.FileMode.Create))
                {
                    BinaryWriter bw = new BinaryWriter(fs);

                    bw.Write(Options.Global.MajorVersion);
                    bw.Write(Options.Global.MinorVersion);
                    bw.Write(Options.Global.SubMinorVersion);

                    foreach (KeyValuePair<string, byte[]> prset in Presets)
                    {
                        WriteEntry(bw, prset.Key, prset.Value);
                    }

                    bw.Close();
                    fs.Close();
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(this, "Error while trying to write \"presets.byte\" file. \r\n" + ex.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Writes a packet preset using binary writer
        /// </summary>
        /// <param name="bw"></param>
        /// <param name="ac"></param>
        private void WriteEntry(BinaryWriter bw, string presetName, byte[] preset)
        {
            // сначала запишем размер записи
            bw.Write(sizeof(int) + presetName.Length 
                + sizeof(int) + preset.Length);

            // потом данные
            bw.Write(presetName.Length);
            bw.Write(presetName.ToCharArray());
            bw.Write(preset.Length);
            bw.Write(preset);
        }

        /// <summary>
        /// Loading packet preset map from file
        /// </summary>
        private void LoadAllPresets()
        {
            try
            {
                using (System.IO.FileStream fs = new System.IO.FileStream("presets.byte", System.IO.FileMode.Open))
                {
                    BinaryReader br = new BinaryReader(fs);

                    try
                    {
                        int maj = br.ReadInt32();
                        int min = br.ReadInt32();
                        int smin = br.ReadInt32();

                        bool fileOldVersion;
                        bool fileNewVersion;

                        fileOldVersion = maj < Options.Global.MajorVersion
                            || min < Options.Global.MinorVersion
                            || smin < Options.Global.SubMinorVersion;

                        fileNewVersion = maj > Options.Global.MajorVersion
                            || min > Options.Global.MinorVersion
                            || smin > Options.Global.SubMinorVersion;

                        bool read = true;
                        if (fileNewVersion)
                        {
                            read = false;
                            if (MessageBox.Show(this, "Preset file are from newer version, possible crash, continue reading?",
                            "Possible crash, continue?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                            {
                                read = true;
                            }
                        }

                        while (read)
                        {
                            ReadEntry(br);
                        };
                    }
                    catch (System.IO.EndOfStreamException)
                    {
                        // ok
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(this, "Error reading saved ac map. \r\n" + ex.Message,
                            "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    br.Close();
                    fs.Close();
                }
            }
            catch (System.IO.FileNotFoundException)
            {
                // ok
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(this, "Error opening \"acmap.ma\" file. \r\n" + ex.Message,
                    "Critical error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Reading using binary reader an packet preset entry
        /// </summary>
        /// <param name="br"></param>
        private void ReadEntry(BinaryReader br)
        {
            int entryLen = 0;
            byte[] preset = new byte[0];
            string presetName = "";

            // сколько всего байт в записи
            entryLen = br.ReadInt32();

            if (entryLen > 0)
            {
                // ac name
                int namelen = br.ReadInt32();
                StringBuilder sb = new StringBuilder();
                sb.Append(br.ReadChars(namelen));
                presetName = sb.ToString();

                entryLen -= namelen + sizeof(int);
            }

            if (entryLen > 0)
            {
                // string mac
                int presetLen = br.ReadInt32();
                preset = br.ReadBytes(presetLen);

                entryLen -= presetLen + sizeof(int);
            }

            if (!Presets.ContainsKey(presetName))
            {
                Presets.Add(presetName, preset);
            }

        }

        #endregion

        #region Event handlers

        #region Events : Tab

        private void TabSendPPPoE_Load(object sender, EventArgs e)
        {
            ethPppoeProtoPacket.InitUiBindings(this.textBox_srcMac,
                this.textBox_destMac,
                this.textBox_playloadLength,
                this.textBox_ACName,
                this.textBox_hostUniqueID,
                this.textBox_sessionId,
                this.comboBox_ppoeType,
                this.treeView_pppoetags,
                this.label_src_convt,
                this.label_dst_convt);

            LoadAllPresets();
        }

        void TabSendPPPoE_Disposed(object sender, EventArgs e)
        {
            SaveAllPresets();
        }

        #endregion
        
        #region Events : Controls

        private void button_send_Click(object sender, EventArgs e)
        {
            byte[] packet = ethPppoeProtoPacket.ToBytesArray();
            if (packet != null)
            {
                MainWindow.DebugLog(packet);

                if (!MainWindow.Instance.RawEtherInstance.DoWrite(packet))
                {
                    MainWindow.LogWriteLine("Failed!", Options.Global.LogLevel.Error);
                }
            }
        }

        private void button_swap_Click(object sender, EventArgs e)
        {
            string text = textBox_destMac.Text;
            textBox_destMac.Text = textBox_srcMac.Text;
            textBox_srcMac.Text = text;
        }

        private void button_my_source_Click(object sender, EventArgs e)
        {
            if (sender.Equals(button_bcast_src))
            {
                textBox_srcMac.Text = "FF:FF:FF:FF:FF:FF";
            }
            else if (sender.Equals(button_my_source))
            {
                textBox_srcMac.Text = Network.GetMyMacString(MainWindow.BindedAdapterId);
            }
        }

        private void button_my_desination_Click(object sender, EventArgs e)
        {
            if (sender.Equals(button_bcast_dest))
            {
                textBox_destMac.Text = "FF:FF:FF:FF:FF:FF";
            }
            else if (sender.Equals(button_my_desination))
            {
                textBox_destMac.Text = Network.GetMyMacString(MainWindow.BindedAdapterId);
            }
        }
        
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SavePreset(currentPresetName);
        }

        private void saveAsToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            if (saveAsToolStripMenuItem.DropDownItems.Count != Presets.Keys.Count + 2)
            {
                while (saveAsToolStripMenuItem.DropDownItems.Count > 2)
                {
                    saveAsToolStripMenuItem.DropDownItems.RemoveAt(2);
                }
                foreach (string key in Presets.Keys)
                {
                    ToolStripItem tsi = saveAsToolStripMenuItem.DropDownItems.Add(key);
                    tsi.Click += new EventHandler(tsi_Click_save);
                }
            }

        }

        private void loadToolStripMenuItem1_DropDownOpening(object sender, EventArgs e)
        {
            if (loadToolStripMenuItem.DropDownItems.Count != Presets.Keys.Count + 1)
            {
                while (loadToolStripMenuItem.DropDownItems.Count > 1)
                {
                    loadToolStripMenuItem.DropDownItems.RemoveAt(1);
                }

                foreach (string key in Presets.Keys)
                {
                    ToolStripItem tsi = loadToolStripMenuItem.DropDownItems.Add(key);
                    tsi.Click += new EventHandler(tsi_Click_load);
                }
            }

        }

        private void toolStripTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 0xa || e.KeyChar == 0xd)
            {
                SavePreset(toolStripTextBox_saveasname.Text);
                toolStripTextBox_saveasname.Text = "";
                toolStripTextBox_saveasname.Owner.Hide();
                UpdateUI();
            }
        }

        private void removeToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            if (removeToolStripMenuItem.DropDownItems.Count != Presets.Keys.Count + 1)
            {
                while (removeToolStripMenuItem.DropDownItems.Count > 1)
                {
                    removeToolStripMenuItem.DropDownItems.RemoveAt(1);
                }

                foreach (string key in Presets.Keys)
                {
                    ToolStripItem tsi = removeToolStripMenuItem.DropDownItems.Add(key);
                    tsi.Click += new EventHandler(tsi_Click_Remove);
                }
            }
        }

        void tsi_Click_save(object sender, EventArgs e)
        {
            ToolStripItem clickedItem = (ToolStripItem)sender;

            SavePreset(clickedItem.Text);

            UpdateUI();
        }

        void tsi_Click_load(object sender, EventArgs e)
        {
            ToolStripItem clickedItem = (ToolStripItem)sender;

            LoadPreset(clickedItem.Text);

            UpdateUI();
        }

        void tsi_Click_Remove(object sender, EventArgs e)
        {
            ToolStripItem clickedItem = (ToolStripItem)sender;

            RemovePreset(clickedItem.Text);

            if (clickedItem.Text == currentPresetName)
            {
                currentPresetName = "Default";
            }

            UpdateUI();
        }
            
        #endregion

        #endregion
    }
}
